import { defineComponent, ExtractPropTypes, InjectionKey, watch, PropType, provide, computed, Ref, ComputedRef } from 'vue'
import { Value, MenuGroupOption, MenuOption, Option } from './interface'
import useMemo from '../_util/hooks/useMemo'
import { createItem, getNonLeafKeys, treeFindPath } from './utils'

const menuProps = {
  prefixCls: { default: 'x-menu' },
  indent: { type: Number, default: 20 },
  value: [String, Number] as PropType<Value>,
  defaultExpandAll: Boolean,
  expandedKeys: Array as PropType<Value[]>,
  mode: { type: String as PropType<'vertical' | 'horizontal' | 'inline'>, default: 'inline' },
  options: { type: Array as PropType<Option[]>, default: () => [] as Option[] },
  collapsed: { type: Boolean, default: false },
  borderLight: { type: Boolean, default: true },
  disabled: Boolean,
  selectable: { default: true }
}

export type MenuProps = ExtractPropTypes<typeof menuProps>

export type MenuContextProps = {
  props: MenuProps
  valueRef: Ref<Value>
  expandedKeysRef: Ref<Value[]>
  activePathRef: Ref<Value[]>
  collapsedRef: ComputedRef<boolean>
  onSelect: (item: MenuOption) => void
  onExpanded: (item: MenuOption) => void
}

export const MenuContextKey: InjectionKey<MenuContextProps> = Symbol('menu')

export default defineComponent({
  name: 'x-menu',
  props: menuProps,
  emits: ['update:value', 'update:expandedKeys', 'expandChange', 'select', 'click'],
  setup(props, { emit }) {
    const expandedKeys = props.defaultExpandAll ? getNonLeafKeys(props.options) : []
    const expandedKeysRef = useMemo(() => props.expandedKeys || expandedKeys)

    const valueRef = useMemo(() => props.value)
    const activePathRef = computed(() => treeFindPath(props.options, ({ value }) => value === valueRef.value))

    watch(
      () => props.collapsed,
      () => [onExpandChange([])]
    )

    // ========================== Event ==========================
    function onSelect(item: MenuOption) {
      emit('click', item)
      if (props.collapsed || props.mode === 'horizontal' || props.mode === 'vertical') {
        onExpandChange([])
      }

      if (!props.selectable) return
      const val = item.value
      valueRef.value = val
      emit('update:value', val)
      emit('select', item)
    }
    function onExpanded(item: MenuOption) {
      const val = item.value
      const i = expandedKeysRef.value.findIndex(e => e == val)
      if (~i) {
        expandedKeysRef.value.splice(i, 1)
      } else {
        expandedKeysRef.value.push(val)
      }
      onExpandChange(expandedKeysRef.value)
    }
    function onExpandChange(arr: Value[]) {
      emit('update:expandedKeys', (expandedKeysRef.value = arr))
      emit('expandChange', arr)
    }

    // ========================== Provide ==========================
    provide(MenuContextKey, {
      props,
      valueRef,
      expandedKeysRef,
      activePathRef,
      collapsedRef: computed(() => props.collapsed),
      onSelect,
      onExpanded
    })

    return () => {
      const { prefixCls } = props
      const cls = {
        [prefixCls]: true,
        [`${prefixCls}-collapsed`]: props.collapsed,
        [`${prefixCls}-${props.mode}`]: true
      }

      return (
        <div class={cls} role='menu'>
          {props.options.map(createItem)}
        </div>
      )
    }
  }
})
